/**
*	Functions responsible for determining the local context for the OK algorithm.
*
*	author: Maciej Rubikowski
*/

#include "ok.h"

typedef std::pair<ObjectSet, FormalContext*> ObjectContextPair;
typedef std::vector<ObjectContextPair> LocalContextGrouping;

FormalContext* OKLocalContext(const OAPair &oa, const FormalContext * const P) {
	FormalContext *FC = new FormalContext;
	LocalContextGrouping G;
	ObjectSet *O = oa.first;
	AttributeSet *A = oa.second;

	ObjectContextPair ocp = std::pair<ObjectSet, FormalContext*>(ObjectSet(), new FormalContext(*P));
	G.push_back(ocp);

	for (std::set<Object>::const_iterator o = O->data.begin(); o != O->data.end(); ++o) {
		LocalContextGrouping set_S;
		ObjectSet *Z = NULL;
		FormalContext *E = NULL;
		for (std::vector<ObjectContextPair>::iterator j = G.begin(); j != G.end();) {
			Z = &j->first;
			E = j->second;
			ObjectSet os = *Z;
			os.data.insert(*o);
			ObjectContextPair S = std::pair<ObjectSet, FormalContext*>(os, new FormalContext);	
			for (std::vector<OAPair>::iterator YB = E->data.begin(); YB != E->data.end(); ) {
				// remove all y from Y such that y precedes o
				// and BTW check if o belongs to Y
				ObjectSet *Y = YB->first;
				AttributeSet *B = YB->second;

				bool oInY = false;
				for (std::set<Object>::iterator y = Y->data.begin(); y != Y->data.end(); ) {
					if (*y < *o)
						y = Y->data.erase(y);
					else if (*y == *o) {
						oInY = true;
						break;
					} else {
						++y;
					}
				}
				// if object o belongs to Y...
				if (oInY) {
					Y->data.erase(*o); // remove o from Y
					S.second->data.push_back(*YB); 
					YB = E->data.erase(YB); // remove (Y, B) from E
				} else
					++YB;
			}
			// if the second element of S is different from null_set then
			if (!S.second->data.empty()) {
				set_S.push_back(S);
				if (E->data.empty()) {
					j = G.erase(j);
					delete E;
				} else {
					++j;
				}
			} else {
				delete S.second;
				++j;
			}
		}
		G.insert(G.end(), set_S.begin(), set_S.end());
	}

	// prepare P<O, A>
	for (LocalContextGrouping::iterator it = G.begin(); it != G.end();) {
		if (it->second->data.empty()) {
			++it;
			continue;
		}
		ObjectSet *Z = new ObjectSet(it->first);
		AttributeSet *D = new AttributeSet;
		for (std::vector<OAPair>::iterator it2 = it->second->data.begin(); it2 != it->second->data.end(); ++it2)
			D->data.insert(it2->second->data.begin(), it2->second->data.end());
		D->processed = false;
		FC->data.push_back(OAPair(Z, D));
		++it;
	}
	// clean up
	for(LocalContextGrouping::iterator it = G.begin(); it != G.end(); ++it) {
		for(std::vector<OAPair>::iterator it2 = it->second->data.begin(); it2 != it->second->data.end(); ++it2) {
			delete it2->first;
			delete it2->second;
		}
		delete it->second;
	}
	return FC;
}






FormalContext *OKLocalContextMinSup(const OAPair &oa, const FormalContext * const P, unsigned int minSup) {
	LocalContextGrouping G;
	FormalContext *FC = new FormalContext;
	ObjectSet *O = oa.first;
	AttributeSet *A = oa.second;

	// prepare the initial grouping 
	FormalContext *FC2 = new FormalContext;
	for (std::vector<OAPair>::const_iterator it = P->data.begin(); it != P->data.end(); ++it) {
		if (*(oa.second) != *(it->second)) {
			OAPair oap = OAPair(new ObjectSet(*it->first), new AttributeSet(*it->second));
			FC2->data.push_back(oap);
		}
	}

	ObjectContextPair ocp = std::pair<ObjectSet, FormalContext*>(ObjectSet(), FC2);
	G.push_back(ocp);

	for (std::set<Object>::const_iterator o = O->data.begin(); o != O->data.end(); ++o) {
		LocalContextGrouping set_S;
		ObjectSet *Z = NULL;
		FormalContext *E = NULL;
		for (std::vector<ObjectContextPair>::iterator j = G.begin(); j != G.end();) {
			Z = &j->first;
			E = j->second;

			ObjectSet os = *Z;
			os.data.insert(*o);
			ObjectContextPair S = std::make_pair<ObjectSet, FormalContext*>(os, new FormalContext);

			for (std::vector<OAPair>::iterator YB = E->data.begin(); YB != E->data.end();) {
				// remove all y from Y such that y precedes o
				// and BTW check if o belongs to Y
				ObjectSet *Y = YB->first;
				AttributeSet *B = YB->second;

				bool oInY = false;
				for (std::set<Object>::iterator y = YB->first->data.begin(); y != YB->first->data.end();) {
					if (*y < *o)
						y = Y->data.erase(y);
					else if (*y == *o) {
						oInY = true;
						break;
					} else {
						++y;
					}
				}
				// if object o belongs to Y...
				if (oInY) {
					Y->data.erase(*o); // remove o from Y

					S.second->data.push_back(*YB);
					YB = E->data.erase(YB); // remove (Y, B) from E
				} else
					++YB;
			}
			// if the second element of S is different from null_set then
			if (!S.second->data.empty()) {
				set_S.push_back(S);
				if (E->data.empty()) {
					j = G.erase(j);
					delete E;
				} else {
					++j;
				}
			} else {
				delete S.second;
				++j;
			}
		}
		G.insert(G.end(), set_S.begin(), set_S.end());
	}
	// prepare P<O, A>
	for (LocalContextGrouping::iterator it = G.begin(); it != G.end();) {
		bool isProcessed = false;
		if (it->second->data.empty() || it->first.data.size() < minSup) {
			++it;
			continue;
		}
		ObjectSet *Z = new ObjectSet(it->first);
		AttributeSet *D = new AttributeSet;
		for (std::vector<OAPair>::iterator it2 = it->second->data.begin(); it2 != it->second->data.end(); ++it2) {
			D->data.insert(it2->second->data.begin(), it2->second->data.end());
			isProcessed = it2->second->processed;
		}
		D->processed = isProcessed;
		if (isProcessed) {
			++(FC->processedNo);
			//FC->data.push_back(OAPair(Z, D));
			std::vector<OAPair>::iterator iter=FC->data.begin();
			for(; iter!=FC->data.end(); ++iter){
				if(!iter->second->processed){
					break;
				}
			}
			FC->data.insert(iter,OAPair(Z,D));
		}else{
			FC->data.push_back(OAPair(Z, D));
		}
		++it;
	}
	// clean up
	for(LocalContextGrouping::iterator it = G.begin(); it != G.end(); ++it) {
		for(std::vector<OAPair>::iterator it2 = it->second->data.begin(); it2 != it->second->data.end(); ++it2) {
			delete it2->first;
			delete it2->second;
		}
		delete it->second;
	}
	return FC;
}